package com.pignest.framework.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.type.Type;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.pignest.hotel.util.PageResult;

@SuppressWarnings("unchecked")
public abstract class BaseHibernateDao extends HibernateDaoSupport implements
		Dao {
	protected final Log log = LogFactory.getLog(getClass());

	public String defaultOrderBy() {
		return null;
	}

	public void insert(Object o) {
		getHibernateTemplate().save(o);
	}

	public void update(Object o) {
		getHibernateTemplate().update(o);
	}

	public void insertOrUpdate(Object o) {
		getHibernateTemplate().saveOrUpdate(o);
	}

	public void saveAll(Collection<?> entities) {
		getHibernateTemplate().saveOrUpdateAll(entities);
	}

	public <T> T get(Class<T> clazz, Serializable id) {
		return (T) getHibernateTemplate().get(clazz, id);
	}
	
	public <T> T get(Class<T> clazz, String key, Object value) {
		StringBuilder hql = new StringBuilder("from ");
		hql.append(clazz.getName()).append(" as o where o.");
		hql.append(key).append("=?");
		List results = getHibernateTemplate().find(hql.toString(), value);
		if (results == null || results.isEmpty())
			return null;
		T o = (T) results.get(0);
		if (results.size() > 1) {
			System.out.println("more than one result!");
		}
		return o;
	}


	public <T> List<T> listAll(Class<T> clazz) {
		String orderBy = defaultOrderBy();
		if (StringUtils.isNotEmpty(orderBy)) {
			String hql = "from " + clazz.getSimpleName() + " as o order by "
					+ orderBy;
			return find(hql);
		}
		return getHibernateTemplate().loadAll(clazz);
	}

	public void delete(Object o) {
		getHibernateTemplate().delete(o);
	}

	public void deleteAll(Collection<?> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	public <T> void delete(Class<T> clazz, Serializable id) {
		getHibernateTemplate().delete(get(clazz, id));
	}

	public <T> List<T> findByNamedQuery(String queryName, Object[] values) {
		return (List<T>) getHibernateTemplate().findByNamedQuery(queryName,
				values);
	}

	public <T> List<T> findByNamedQuery(String queryName) {
		return (List<T>) getHibernateTemplate().findByNamedQuery(queryName);
	}

	public <T> List<T> findByNamedQueryNoCache(final String queryName,
			final Object[] values) throws DataAccessException {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query queryObject = session.getNamedQuery(queryName);
						queryObject.setCacheable(false);
						SessionFactoryUtils.applyTransactionTimeout(
								queryObject, getSessionFactory());
						if (values != null) {
							for (int i = 0; i < values.length; i++) {
								queryObject.setParameter(i, values[i]);
							}
						}
						return queryObject.list();
					}
				}, true);
	}

	public <T> List<T> findByHQLUseIterator(final String sHql,
			final String[] paramNames, final Object[] paramValues,
			final int firstResult, final int maxResults) {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = session.createQuery(sHql);
						if (null != paramNames) {
							for (int i = 0; i < paramNames.length; i++) {
								query.setParameter(paramNames[i],
										paramValues[i]);
							}
						}
						if (0 < firstResult && 0 < maxResults) {
							query.setFirstResult(firstResult);
							query.setMaxResults(maxResults);
						}
						List result = maxResults > 0 ? new ArrayList(maxResults)
								: new ArrayList();
						Object o;
						for (Iterator it = query.iterate(); it.hasNext();) {
							o = it.next();
							// 初始化属性！
							o.toString();
							result.add(o);
						}
						return result;
					}
				});
	}

	public <T> List<T> findByHQLUseList(final String sHql,
			final String[] paramNames, final Object[] paramValues,
			final int firstResult, final int maxResults) {

		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = session.createQuery(sHql);
						if (null != paramNames) {
							for (int i = 0; i < paramNames.length; i++) {
								query.setParameter(paramNames[i],
										paramValues[i]);
							}
						}
						if (0 <= firstResult && 0 < maxResults) {
							query.setFirstResult(firstResult);
							query.setMaxResults(maxResults);
						}
						return query.list();
					}
				});
	}

	public <T> List<T> find(String sHql) {
		return findByHQLUseList(sHql, null, null, -1, -1);
	}

	public <T> List<T> findByHQLUseListNoCache(final String sHql,
			final String[] paramNames, final Object[] paramValues,
			final int firstResult, final int maxResults) {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = session.createQuery(sHql);
						query.setCacheable(false);
						if (null != paramNames) {
							for (int i = 0; i < paramNames.length; i++) {
								query.setParameter(paramNames[i],
										paramValues[i]);
							}
						}
						if (0 <= firstResult && 0 < maxResults) {
							query.setFirstResult(firstResult);
							query.setMaxResults(maxResults);
						}
						return query.list();
					}
				});
	}

	public <T> List<T> findByNativeSQL(final String sql,
			final String[] columnAliases) {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						SQLQuery query = session.createSQLQuery(sql);
						query.setCacheable(false);
						if (columnAliases != null && columnAliases.length > 0) {
							for (String columnAlias : columnAliases) {
								query.addScalar(columnAlias, Hibernate.STRING);
							}
						}
						return query.list();
					}
				});
	}

	public <T> List<T> findByNativeSQL(final String sql,
			final String[] columnAliases, final int firstResult,
			final int maxResults) {
		return (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						SQLQuery query = session.createSQLQuery(sql);
						query.setCacheable(false);
						if (columnAliases != null && columnAliases.length > 0) {
							for (String columnAlias : columnAliases) {
								query.addScalar(columnAlias, Hibernate.STRING);
							}
						}
						if (0 <= firstResult && 0 < maxResults) {
							query.setFirstResult(firstResult);
							query.setMaxResults(maxResults);
						}
						return query.list();
					}
				});
	}

	public <T> List<T> findByCriteria(DetachedCriteria detachedCriteria) {
		return getHibernateTemplate().findByCriteria(detachedCriteria);
	}

	public <T> List<T> findByCriteria(DetachedCriteria detachedCriteria,
			final int firstResult, final int maxResults)
			throws DataAccessException {
		return getHibernateTemplate().findByCriteria(detachedCriteria,
				firstResult, maxResults);
	}

	public int update(final String hql) {
		return getHibernateTemplate().bulkUpdate(hql);
	}
	
	public int updateByHQL(final String hql, final String[] paramNames,
			final Object[] paramValues) {
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = session.createQuery(hql);
						if (null != paramNames) {
							for (int i = 0; i < paramNames.length; i++) {
								query.setParameter(paramNames[i],
										paramValues[i]);
							}
						}
						return query.executeUpdate();
					}
				});
	}

	/**
	 * 使用hql 语句进行分页操作
	 * 
	 * @param hql
	 * @param start
	 * @param length
	 * @return List
	 */
	public <T> List<T> getListForPage(final String hql, final int start,
			final int length) {
		List list = getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(org.hibernate.Session session)
					throws HibernateException, SQLException {
				org.hibernate.Query query = session.createQuery(hql);
				query.setFirstResult(start);
				query.setMaxResults(length);
				List<T> list = query.list();
				return list;
			}
		});
		return list;
	}

	
	/**
	 * HQL查询可带分页可带参数
	 * @param hql
	 * @param Where
	 * @param pageResult 
	 * @param isCachEnable 是否缓存
	 * @return 
	 */
	public List findByHQL(final String hql , final Object[] param, final PageResult page, final boolean isCachEnable) {
		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException {
				Query query = session.createQuery(hql);
				if (param != null) {
					for (int i = 0; i < param.length; i++) {
						query.setParameter(i, param[i]);
					}
				} 
				if (page.getPageSize() > 0)
					query.setMaxResults(page.getPageSize());
				if (page.getBeginIndex() > 0)
					query.setFirstResult(page.getBeginIndex());
				// 使用查询缓存
				query.setCacheable(isCachEnable);
				return query.list();
			}
		});
	}
	
	@Override
	public <T> int count(final Class<T> clazz, final String search, final Object[] values, final Type[] types) {
		return (int)(long)(Long)getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				StringBuilder hql = new StringBuilder();
				hql.append("select count(*) from ").append(clazz.getName());
				if (StringUtils.isNotEmpty(search)) {
					hql.append(" where ").append(search);
				}
				Query q = session.createQuery(hql.toString());
				if (search != null && values != null && values.length > 0) {
					if (types == null || types.length < values.length) {
						for (int i = 0; i < values.length; i++) {
							q.setParameter(i, values[i]);	//not null
						}
					} else {
						q.setParameters(values, types);
					}
				}
				return q.uniqueResult();
			}
		});
	}

	
	public <T> List<T> query(final Class<T> clazz, final String search, 
			final int pageSize, final int pageIndex, final Object[] values, final Type[] types) {
		return (List<T>)getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				StringBuilder hql = new StringBuilder();
				hql.append("from ").append(clazz.getName()).append(" as o");
				if (StringUtils.isNotEmpty(search)) {
					if (search.startsWith("order by"))
						hql.append(" ");
					else
						hql.append(" where ");
					hql.append(search);
				}
//				System.out.println(hql);
				Query q = session.createQuery(hql.toString());
				if (search != null && values != null && values.length > 0) {
					if (types == null || types.length < values.length) {
						for (int i = 0; i < values.length; i++) {
							q.setParameter(i, values[i]);	//not null
						}
					} else {
						q.setParameters(values, types);
					}
				}
				if (pageSize > 0 && pageIndex >= 0) {
					q.setFirstResult(pageSize * pageIndex);
					q.setMaxResults(pageSize);
					q.setFetchSize(pageSize);
				}
				return q.list();
			}
		});
	}


	public void flush() {
		getHibernateTemplate().flush();
	}
}